Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ

Node / meshtastic / Meshtastic-Android / files / core / common / src / commonTest / kotlin / org / meshtastic / core / common / log / ExpectedConditionTest.kt

Displaying Raw β€’ Download

core/common/src/commonTest/kotlin/org/meshtastic/core/common/log/ExpectedConditionTest.kt 5235b8fc51eb5f46d453effb2deec00bcc6abdbb (5235b8fc) Text, 6.73 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.common.log

Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787kotlinx.coroutines.CancellationException
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertTrue

T8b949e/**
* Locks down the reporting contract that both Crashlytics and Datadog RUM consult.
*
* Regressions here are silent and expensive: they either bury real crashes under expected-condition noise, or stop a
* genuine defect from being reported at all.
*/
Tff7b72class T56d364ExpectedConditionTest Tb4b4b4{

Tff7b72private Tff7b72class T56d364TestExpectedTb4b4b4(Tff7b72override Tff7b72val Te6edf3expectedConditionLabelTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6fftest-expectedTa5d6ff"Tb4b4b4) Tb4b4b4:
Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffexpectedTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3ExpectedCondition

T8b949e// ── shouldReportAsException ──────────────────────────────────────────────────────

Tf0883e@Test
Tff7b72fun Td2a8ff`severities below Error are never reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3severities Tff7b72= Te6edf3listOfTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3VerboseTb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3DebugTb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3InfoTb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4)
Te6edf3severitiesTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3severity Tff7b72-Tff7b72>
Te6edf3assertFalseTb4b4b4(
Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3severityTb4b4b4, Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffboomTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4,
Ta5d6ff"Tffd700$Te6edf3severityTa5d6ff with a throwable must not be reportedTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3severityTb4b4b4, Tff7b72nullTb4b4b4)Tb4b4b4, Ta5d6ff"Tffd700$Te6edf3severityTa5d6ff without a throwable must not be reportedTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`genuine errors are still reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertTrueTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffboomTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3AssertTb4b4b4, Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffboomTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`error without a throwable is still reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Crashlytics synthesises an Exception(message) for these, so the gate must stay open.
Te6edf3assertTrueTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Tff7b72nullTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`cancellation is never reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3CancellationExceptionTb4b4b4(Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3AssertTb4b4b4, Te6edf3CancellationExceptionTb4b4b4(Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`expected conditions are never reported even at error severity`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3AssertTb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`expected condition wrapped in a plain exception is not reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3wrapped Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6fftransport failedTa5d6ff"Tb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3wrappedTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`a genuine failure carrying a cancellation cause is still reported`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Coroutine machinery routinely attaches a cancellation as the cause of an unrelated real failure.
T8b949e// Unwrapping the chain for CancellationException would silently drop these reports.
Tff7b72val Te6edf3wrapped Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffwrite failedTa5d6ff"Tb4b4b4, Te6edf3CancellationExceptionTb4b4b4(Ta5d6ff"Ta5d6ffscope closedTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3wrappedTb4b4b4)Tb4b4b4)
Tb4b4b4}

T8b949e// ── Datadog downgrade rule (deliberately differs on cancellation) ────────────────

Tf0883e@Test
Tff7b72fun Td2a8ff`datadog downgrades expected conditions`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertTrueTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3AssertTb4b4b4, Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffouterTa5d6ff"Tb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`datadog keeps cancellation at error even though crashlytics drops it`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Load-bearing asymmetry: a cancellation logged at error means a call site swallowed it instead of
T8b949e// rethrowing β€” broken structured concurrency, and a real bug. This is the signal that surfaced #6468.
Tff7b72val Te6edf3cancellation Tff7b72= Te6edf3CancellationExceptionTb4b4b4(Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldReportAsExceptionTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3cancellationTb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3cancellationTb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`datadog leaves genuine errors and sub-error severities alone`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertFalseTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffboomTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3ErrorTb4b4b4, Tff7b72nullTb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3shouldDowngradeForDatadogTb4b4b4(Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tb4b4b4}

T8b949e// ── cause-chain walking ──────────────────────────────────────────────────────────

Tf0883e@Test
Tff7b72fun Td2a8ff`label is read from the throwable itself`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6fftest-expectedTa5d6ff"Tb4b4b4, Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3TestExpectedTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`label is read through the cause chain`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3wrapped Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffouterTa5d6ff"Tb4b4b4, Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffmiddleTa5d6ff"Tb4b4b4, Te6edf3TestExpectedTb4b4b4(Ta5d6ff"Ta5d6ffble-bluetooth-disabledTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffble-bluetooth-disabledTa5d6ff"Tb4b4b4, Te6edf3wrappedTb4b4b4.Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3wrappedTb4b4b4.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`plain throwable has no label`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3plain Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffouterTa5d6ff"Tb4b4b4, Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffinnerTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3plainTb4b4b4.Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3plainTb4b4b4.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`cause chain walking is depth limited`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// A chain longer than the cap must terminate rather than spin, even though the marker is out of reach.
Tff7b72var Te6edf3deepTb4b4b4: Te6edf3Throwable Tff7b72= Te6edf3TestExpectedTb4b4b4(Tb4b4b4)
Te6edf3repeatTb4b4b4(T79c0ff5T79c0ff0Tb4b4b4) Tb4b4b4{ Te6edf3deep Tff7b72= Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6fflayerTa5d6ff"Tb4b4b4, Te6edf3deepTb4b4b4) Tb4b4b4}
Te6edf3assertNullTb4b4b4(Te6edf3deepTb4b4b4.Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3deepTb4b4b4.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`self referential cause chain terminates`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3selfReferencing Tff7b72=
Tff7b72object Tb4b4b4: T56d364RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffloopTa5d6ff"Tb4b4b4) Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3causeTb4b4b4: Te6edf3Throwable
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Tff7b72this
Tb4b4b4}
Te6edf3assertNullTb4b4b4(Te6edf3selfReferencingTb4b4b4.Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.06s